/* From N6QW using the 160 X 128 TFT Color display with the Si5351 Clock Generator. The sketch includes selectable USB/LSB, a Tune function with 988 Hz tone and the Fout is 1X the LO ( June 15,2018 Modified to include the two frequewncy VFO Scheme. TFT pinout information The VCC Pin is not used LED = 3.3 Volts DC SCK = Pin D13 SDA = Pin D11 AO = Pin D9 Reset = Pin D8 CS = Pin D10 GND = Ground Vcc = Not Used Tone = Pin D6 LED = Pin D7 SW = Pin A1 Selects Upper and Lower Sideband SW1 = Pin A2 Slects TUNE Encoder Button = Pin A3 Encoder A = Pin D2 Encoder B = Pin D3 LED = Pin D7 VFO = Pin A0 USB/LSB = A1 Tune = A2 Step = A3 SDA = A4 SCL = A5 VFO Select = D5 digitally selects which VFO to use- SM = A6 //S Meter pin TX = D4 // Keys the Transmit */ #include #include #include // Hardware-specific library #include "Rotary.h" #include #define TONE_PIN 6 #define NOTE_B5 988 #if defined(__SAM3X8E__) #undef __FlashStringHelper::F(string_literal) #define F(string_literal) string_literal #endif #define __CS 10 #define __DC 9 #define __RST 8 #define ENCODER_B 2 // Encoder pin A #define ENCODER_A 3 // Encoder pin B #define ENCODER_BTN A3 // Color definitions #define BLACK 0x0000 #define BLUE 0x001F #define RED 0xF800 #define GREEN 0x07E0 #define CYAN 0x07FF #define MAGENTA 0xF81F #define YELLOW 0xFFE0 #define WHITE 0xFFFF Adafruit_ST7735 display = Adafruit_ST7735(__CS, __DC, __RST); #include "si5351.h" #include "Wire.h" Si5351 si5351; long int frq; int_fast32_t rx = 16198500L; // Starting frequency of VFO operating frequency plus offset which is selectable IF = 9.0 MHz int_fast32_t rx2= 0L; // variable to hold the updated frequency int_fast32_t increment = 100; // starting VFO update increment in HZ. int_fast32_t bfo = 8998500L; // default offset sideband inversion thus USB int_fast32_t rx1 = 16198500L; int_fast32_t rx3 = 16198500L; int_fast32_t vfoA = 1; int_fast32_t vfoB = 1; int_fast32_t old_vfoA = 1; int_fast32_t old_vfoB = 1; volatile uint32_t oldAB = 1; volatile uint32_t vfo = 1; int RunOnce = 1; String hertz = " 100"; byte ones,tens,hundreds,thousands,tenthousands,hundredthousands,millions ; //Placeholders int buttonstate = 0; int buttonstate2 = 0; const int tonepin = 6; const int SW = A1; //selects upper or lower sideband const int SW1 = A2; // provides the TUNE fucntion const int LED = 7; //Controls the transmitter in TUNE simple 5 VDC reed relay int backlight = 0; int i = 0; const int VFO = A0; // Analog Pin A0 for the S Meter function int val = 0; int val1 = 0; // not used int val2 = 0; // ditto int val5 = 0; // not used int val6 = 0; // ditto int z = 0; // no longer used const int TX = 4; //USE VFO A only on transmit const int VS = 5; //digital select of VFO important for transmit select int adcval = A6; // Analog Pin A6 for the S Meter function int buttonState = 0; int lastButtonState = 0; Rotary r = Rotary(2,3); // sets the pins the rotary encoder uses. Must be interrupt pins. void setup() { Serial.begin(9600); PCICR |= (1 << PCIE2); PCMSK2 |= (1 << PCINT18) | (1 << PCINT19); sei(); //display.begin(); display.initR(INITR_BLACKTAB); display.setRotation(1); // landsacape versus portrait display.fillScreen(BLUE); pinMode(SW, INPUT); digitalWrite(SW,HIGH); // Selects either USB or LSB````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````[ pinMode(SW1, INPUT); digitalWrite(SW1,HIGH);//Tune pinMode(LED, OUTPUT);digitalWrite(LED,LOW); pinMode(backlight, OUTPUT); pinMode(A3,INPUT); digitalWrite(A3,HIGH);// Connect to a button that goes to GND on push pinMode(A1,INPUT); digitalWrite(A1,HIGH); pinMode(A0, INPUT); pinMode(A6,INPUT); //digitalWrite(A6, HIGH); //Problem with S Meter reading pinMode(TX, INPUT); digitalWrite(TX, HIGH); pinMode(VS, INPUT); digitalWrite(VS,HIGH); pinMode(7,OUTPUT); // init done // set backlight level //while (!Serial); //tft.begin(); #if defined(__MK20DX128__) || defined(__MK20DX256__) display.setBitrate(24000000); #endif // initialize the Si5351 si5351.init(SI5351_CRYSTAL_LOAD_8PF); si5351.set_correction(100); si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); si5351.set_freq(bfo, 0, SI5351_CLK2); si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_8MA); } void setincrement(){ if (increment == 10){increment = 100; hertz = "100";} else if (increment == 100){increment = 1000; hertz=" 1K";} else if (increment == 1000){increment = 10000; hertz=" 10K"; } else if (increment == 10000){increment = 100000; hertz="100K";} // else if (increment == 100000){increment = 1000000; hertz=" 1M";} else{increment = 10; hertz = " 10";}; delay(100); // Adjust this delay to speed up/slow down the button menu scroll speed. } void showFreq1(){ display.fillCircle(12 ,15, 12,YELLOW); display.fillRect(15,3,130,25,YELLOW); // The 2nd X and Y are the depth and width not the coordinates display.fillCircle(146 ,15, 12,YELLOW); millions = int(((rx1)-bfo)/1000000); hundredthousands = ((((rx1)-bfo )/100000)%10); tenthousands = ((((rx1)-bfo )/10000)%10); thousands = ((((rx1)-bfo )/1000)%10); hundreds = ((((rx1)-bfo )/100)%10); tens = ((((rx1)-bfo )/10)%10); ones = ((((rx1)-bfo )/1)%10); display.setTextSize(3); display.setTextColor(BLACK); if (millions > 9){display.setCursor(14,5);} else{display .setCursor(22,5);} display.print(millions); display.print(hundredthousands); display.print(tenthousands); display.print(thousands); display.setTextColor(BLACK); display.setTextSize(2); display.print(hundreds); display.print(tens); display.print(ones); } void showFreq2(){ display.fillCircle(12 ,110, 12,YELLOW); display.fillRect(15,98,130,25,YELLOW); display.fillCircle(146 ,110, 12,YELLOW); millions = int(((rx3)-bfo)/1000000); hundredthousands = ((((rx3)-bfo )/100000)%10); tenthousands = ((((rx3)-bfo )/10000)%10); thousands = ((((rx3)-bfo )/1000)%10); hundreds = ((((rx3)-bfo )/100)%10); tens = ((((rx3)-bfo )/10)%10); ones = ((((rx3)-bfo )/1)%10); display.setTextSize(3); display.setTextColor(BLACK); if (millions > 9){display.setCursor(14,100);} else{display .setCursor(22,100);} display.print(millions); display.print(hundredthousands); display.print(tenthousands); display.print(thousands); display.setTextColor(BLACK); display.setTextSize(2); display.print(hundreds); display.print(tens); display.print(ones); } ISR(PCINT2_vect) { unsigned char result = r.process(); if (result) { if (result == DIR_CW){rx=rx+(1*increment);} // account that rx = 1 times the frequency so the increment must be 4* or /.25 else {rx = rx-(1*increment);}; if (rx >=40100000){rx=rx2;}; // UPPER VFO LIMIT = 1X the opearting frequency + offset if (rx <=9100000){rx=rx2;}; // LOWER VFO LIMIT = 1 X the opearting frequency + Offset } } //*********************************** LOOP *********************** void loop() { // The loop is small as all action are sub routines SplashScreen(); RunOnce = 0; display.setRotation(1); // landsacape versus portrait CheckMode(); //********Moved void checkBand out of the loop CheckSideBand(); // CheckVFO(); //tells which vfo to use but is logic selected versus switch selected CheckFreq(); CheckDisplay(); CheckSM(); CheckTX(); CheckVS(); // lastButtonState = buttonState; display.setCursor(116,43); display.setTextColor(YELLOW); display.setTextSize(1); display.print(hertz); buttonstate = digitalRead(A3); if(buttonstate == LOW) { setincrement(); display.fillRect(110, 40, 140, 10, BLUE); display.setCursor(116,43); display.setTextColor(BLUE); // blanks out the old setting display.setTextSize(1); display.print(hertz); delay(10); } delay(25); } //End of Loop //*******************************Tune Function Check Mode ************************* void CheckMode(){ buttonState = digitalRead(SW1); // creates a 10 second tuning pulse trani 50% duty cycle and makes TUNE appear on the screen if(buttonState != lastButtonState){ if(buttonState == LOW){ si5351.set_freq(vfoA , SI5351_PLL_FIXED, SI5351_CLK0); digitalWrite(LED,HIGH); display.setTextSize(1); display.setTextColor(0x07D9); display.setCursor(70, 60); display.print("TUNE"); digitalWrite(A0, HIGH); display.setTextColor(WHITE); display.setTextSize(1); display.setCursor(70,44); display.print(rx1-bfo); display.fillCircle(152, 57, 3, GREEN); delay(12); for(int i = 0; i < 100; i++) { tone(6, NOTE_B5); delay(50); noTone(6); delay(50); } } else{ digitalWrite(LED,LOW); display.setTextSize(1); // This prints a Black TUNE over the RED TUNE and makes it disappear from the scereen display.setTextColor(BLUE); display.setCursor(70, 60); display.print("TUNE"); display.setTextColor(BLUE); display.setTextSize(1); display.setCursor(70,44); display.print(rx1-bfo); noTone(6); } delay(50); } } //******************************** Check SideBand ****************************** void CheckSideBand() { if(digitalRead(SW)){ //********If SW is true do the following. //Upper Sideband --- Sideband Inversion bfo = 9001500L; si5351.set_freq( bfo, 0, SI5351_CLK2);{ if (bfo = 9001500L); display.fillCircle(54,52, 3, RED); //shows which sideband is selected display.fillCircle(54,68, 3, BLUE); } } else{ //**********if not, do this. //LOWER Sideband --sideband inversion bfo = 8998500L; si5351.set_freq( bfo, 0, SI5351_CLK2); display.fillCircle(54,68, 3, RED); // shows whihc sideband is selected display.fillCircle(54,52, 3, BLUE); //shows which sideband is selected } } // *********************Check Digital VFO Select ***************************** /* by using another pin on the Arduino we can digitally write the VFO selection such as you would have for receiving * on one VFO and TRansmitting on another. Right now you can select whihc VFO reievies but the old logic will oscillate * beteen the two VFO's on transmit if you try to do it direct. Old logic would let you transmit on the VFO * that was doing the receiving. * * */ //*********************VFO A or VFO B *************************** void CheckVS(){ //thus a toggle switch controls writing which VFO indirectly digitalRead(5); if(digitalRead(5) == HIGH){ digitalWrite(A0, HIGH); useVFOA(); } else { digitalWrite(A0, LOW); useVFOB(); } } //***************************************************** Check VFO ********************* /* // Don't use this void CheckVFO() { digitalRead(A0); if(digitalRead(A0)== HIGH){ useVFOA(); showFreq(); z = 1; } else { useVFOB(); showFreq(); z = 2; } } */ // *************************************VFO A******************* void useVFOA(){ // Has logic so you can have the correct display in concert with USB/LSB CheckFreq1(); digitalRead(A0) == HIGH; display.setTextSize(3); rx = rx1; millions = int(((rx)-bfo)/1000000); hundredthousands = ((((rx)-bfo )/100000)%10); tenthousands = ((((rx)-bfo )/10000)%10); thousands = ((((rx)-bfo )/1000)%10); hundreds = ((((rx)-bfo )/100)%10); tens = ((((rx)-bfo )/10)%10); ones = ((((rx)-bfo )/1)%10); display.setTextSize(3); display.setTextColor(BLACK); if (millions > 9){display.setCursor(14,5);} else{display .setCursor(22,5);} display.print(millions); display.print(hundredthousands); display.print(tenthousands); display.print(thousands); display.setTextColor(BLACK); display.setTextSize(2); display.print(hundreds); display.print(tens); display.print(ones); rx1 == vfoA; vfoA = rx1; old_vfoA = vfoA; si5351.set_freq(vfoA , SI5351_PLL_FIXED, SI5351_CLK0); } // ******************************** VFO B ***************************** void useVFOB(){ CheckFreq2(); digitalRead(A0) == LOW; display.setTextSize(3); rx = rx3; millions = int(((rx)-bfo)/1000000); hundredthousands = ((((rx)-bfo )/100000)%10); tenthousands = ((((rx)-bfo )/10000)%10); thousands = ((((rx)-bfo )/1000)%10); hundreds = ((((rx)-bfo )/100)%10); tens = ((((rx)-bfo )/10)%10); ones = ((((rx)-bfo )/1)%10); display.setTextSize(3); display.setTextColor(BLACK); if (millions > 9){display.setCursor(14,100);} else{display .setCursor(22,100);} display.print(millions); display.print(hundredthousands); display.print(tenthousands); display.print(thousands); display.setTextColor(BLACK); display.setTextSize(2); display.print(hundreds); display.print(tens); display.print(ones); rx3 == vfoB; vfoB = rx3; old_vfoB = vfoB; si5351.set_freq(vfoB , SI5351_PLL_FIXED, SI5351_CLK0); } //**************************************** Frequency Change ************************** void CheckFreq(){ if (rx != rx2){ si5351.set_freq(rx , SI5351_PLL_FIXED, SI5351_CLK0); Serial.println(rx); //si5351.set_freq(bfo , SI5351_PLL_FIXED, SI5351_CLK2); rx2 = rx; } } //************************* Frequency Change 1 ************************* void CheckFreq1(){ if(rx != rx2) { si5351.set_freq(rx1 , SI5351_PLL_FIXED, SI5351_CLK0); si5351.set_freq(bfo , SI5351_PLL_FIXED, SI5351_CLK2); rx1 = rx; showFreq1(); } } //************************* Check Frequency 2 ******************************* void CheckFreq2(){ if (rx != rx2){ //This is key to separating the frequencies si5351.set_freq(rx3 , SI5351_PLL_FIXED, SI5351_CLK0); si5351.set_freq(bfo , SI5351_PLL_FIXED, SI5351_CLK2); rx3 = rx; showFreq2(); } } //***********************************CHECK DISPLAY ********************* void CheckDisplay(){ //Sets up the display with stock items display.setCursor(144,43); display.setTextColor(YELLOW); display.setTextSize(1); display.print("Hz"); display.setTextSize(2); display.setTextColor(WHITE); display.setCursor(12,44); display.println("USB"); display.setTextColor(WHITE); display.setCursor(12,62); display.println("LSB"); display.setCursor(122, 54); display.setTextColor(CYAN); display.setTextSize(1); display.print("N6QW"); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(1,33); display.println("Simpleceiver 40M 2 VFO Rig"); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(6,83); display.println("S Meter"); display.setCursor(118,70); display.print("S9"); display.drawRect(56,81,95,13,RED); display.fillRect(57, 82, 93,10, BLACK); display.setTextColor(RED); display.setCursor(135,73); display.print("|"); display.setCursor(62,73); display.print("|"); display.setCursor(76,73); display.print("|"); display.setCursor(88,73); display.print("|"); display.setCursor(104,73); display.print("|"); display.setTextSize(2); //Designating VFO A or B readout location display.setTextColor(MAGENTA); display.setCursor(2,7); display.print("A"); display.setCursor(2,103); display.print("B"); } //************************************* S Meter *************************************** void CheckSM(){ adcval = analogRead(A6); //S meter routine val= 15*log((adcval-200)/20); display.fillRect(58,86, val,2, YELLOW); delay(20); } // ***************************************Check for TRansmit ****************************************** /* This has proven to be a nasty problem for me as I have not been able to implement this in software. Try as I might * the software does not follow what I want to do. I have total functionality with two separate VFO's --my failure is how * to transmit on one while receiving on the other. Finally I hit on a simple hardware solution. Pin A0 is either set high or * low depedning on whether Pin 5 is grounded via a switch. So lets add some hardware in that loop like a NC relay. So here is * how it would work. * 1) In the receiove mode by engaging Pin 5 high or low with an external switch we have a choice of VFO A or VFO B * 2) Let us say we want to make VFO A the Receive and Transmit VFO and VFO B Receive only * 3) When the PTT Switch ground Pin 4 it also causes a NC relay to open. So if you were receiving on VFO B the break in the circuit * would cause the controlling VFO to be VFO A whihc means you would be transmitting on VFO A. * 4) If you purposefully open the VFO A/B sweitch then you would receive and transmit entirely on VFO A. * 5) Adding yet another switch in the loop in the line feeding the juice (or ground) to the NC relay Then you would receive and transmit only on VFO B * 6) Normal Opeartion would entail opening the A/B select swithc so that you would pick the transmit frequency on VFO A. Then closing the switch you would * have VFO A fixed for transmit and tune around on VFO B. Opening the switch then tuning and transmitting is on VFO A */ void CheckTX() { //Green Dot comes on to PTT Also selects which VFO A is used for tranmit digitalRead(4); if(digitalRead(4) == LOW){ display.setCursor(63,44); //We are dsiplayin the received Freq display.setTextSize(1); display.setTextColor(YELLOW); display.println(rx1 - bfo); display.setCursor(63,53); display.println("T=A R=A/B"); display.fillCircle(152, 57, 3, GREEN); digitalWrite(7, HIGH); } else { display.setCursor(63,44); display.setTextSize(1); display.setTextColor(BLUE); display.println(rx1 - bfo); display.setCursor(63,53); display.println("T=A R=A/B"); display.fillCircle(152, 57, 3, BLUE); digitalWrite(7, LOW); CheckVS(); } } //************************************ Splash Screen*********************** void SplashScreen() { // Fun with Run Once Splash Screens if (RunOnce == 1) { display.setTextColor(YELLOW); display.fillScreen(BLACK); display.setCursor(50, 10); display.setTextSize(3); display.println("N6QW"); display.setCursor(40,45); display.setTextSize(1); display.setTextColor(RED); display.setCursor(50, 50); display.println("Illegitimus"); display.setCursor(40, 65); display.println("Non-Carborundum"); delay(5000); display.fillScreen(BLACK); display.setTextColor(YELLOW); display.setTextSize(3); display.setCursor(50,10); display.println("N6QW"); display.setTextSize(1); display.setCursor(40 , 45); display.print("Genius at Work!"); display.setCursor(1, 60); display.println("Simpleceiver 40M 2 VFO Rig"); display.setCursor(20,80); display.print("KNACKER's == Know How!"); display.setCursor(2,105); display.print(" www.n6qwradiogenius.us"); delay(6000); RunOnce = 0; display.fillScreen(BLUE); } }